package com.pray.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.code.kaptcha.Producer;
import com.pray.common.UserHolder;
import com.pray.entity.dto.LoginFormDto;
import com.pray.entity.dto.RegisterDto;
import com.pray.entity.dto.UserDto;
import com.pray.entity.po.Account;
import com.pray.entity.vo.response.AuthorizeVO;
import com.pray.mapper.UserMapper;
import com.pray.service.AccountService;
import com.pray.utils.PrayConstants;
import com.pray.utils.Result;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.pray.utils.PrayConstants.LOGIN_USER_KEY;
import static com.pray.utils.PrayConstants.REGISTER_USER_CODE;

/**
* @author Rainy-Heights
* @description 针对表【user(用户表)】的数据库操作Service实现
* @createDate 2023-08-24 13:00:41
*/
@Service
@Slf4j
//接口集成UserDetailsService重写loadUserByUsername()方法才可以实现自定义登录
public class AccountServiceImpl extends ServiceImpl<UserMapper, Account> implements AccountService, UserDetailsService {

    @Resource
    private Producer producer;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    JavaMailSender mailSender;
    @Value("${spring.account}")
    String sendAccount;
    @Override
    public UserDetails loadUserByUsername(String username){
        Account account = this.findUserByNameOrEmail(username);
        if (account==null){
            throw new RuntimeException("用户名或密码错误");
        }
        return User
                .withUsername(username)
                .password(account.getPassword())
                .build();
    }

    @Override
    public Result register(RegisterDto registerDto) {
        List<Account> list = query().eq("username", registerDto.getUsername())
                .eq("email", registerDto.getEmail()).list();
        if (!list.isEmpty()){
            return Result.fail("用户已存在，请前往登录");
        }
        String isValidated = stringRedisTemplate.opsForValue().get(REGISTER_USER_CODE + registerDto.getEmail());
        if (isValidated==null){
            return Result.fail("请先获取验证码");
        }
        if (!isValidated.equals(registerDto.getCode())){
            return Result.fail("验证码错误");
        }
        Account account = new Account();
        registerDto.setPassword(new BCryptPasswordEncoder().encode(registerDto.getPassword()));
        BeanUtil.copyProperties(registerDto, account);
        save(account);
        return Result.ok(account,"注册成功");
    }

    @Override
    public Result sendCode(String email) {
        String codeKey = REGISTER_USER_CODE + email;
        String code = RandomUtil.randomNumbers(6);

        try {
            SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
            simpleMailMessage.setFrom(sendAccount);
            simpleMailMessage.setTo(email);
            simpleMailMessage.setSubject("【Genshin Impact祈愿活动验证码信息】");
            simpleMailMessage.setText(code);
            mailSender.send(simpleMailMessage);
        }catch (MailException e){
            e.printStackTrace();
            log.error(e.getMessage());
            return Result.message(500,"验证码发送失败，请检查输入邮箱格式");
        }
        stringRedisTemplate.opsForValue().set(codeKey,code,3, TimeUnit.MINUTES);
        return Result.message(200,"验证码发送成功，有效期三分钟");
    }

    @Override
    public Account findUserByNameOrEmail(String text) {
        return query().eq("username",text).or()
                .eq("email",text).one();
    }

    //自从3.0之后抛弃javax.servlet
    @Override
    public void imgValidateCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("image/jpeg");//设置返回数据类型为图片
        String producerText = producer.createText();
        log.info("生成验证码：{}",producerText);

        //验证码放到Redis里面
        stringRedisTemplate.opsForValue().set(PrayConstants.VALID_CODE,producerText);
        BufferedImage bufferedImage=producer.createImage(producerText);//缓冲图片流生成图片验证码
        ServletOutputStream outputStream = response.getOutputStream();

        ImageIO.write(bufferedImage,"jpg",outputStream);//写入到响应输出流里面
        //刷新输出流
        outputStream.flush();
    }
    @Override
    @Deprecated
    public Result login(HttpServletRequest request, LoginFormDto loginFormDto) throws Exception {
        String username = loginFormDto.getUsername();
        String password = loginFormDto.getPassword();
        List<Account> list = query().eq("username", username).eq("password", password).list();

        if (list.isEmpty()){
            return Result.fail("不存在该用户，登录失败");
        }
        Account account = list().get(0);
        UserDto dto = BeanUtil.copyProperties(account, UserDto.class);
        UserHolder.setLocalUser(dto);//存入当前用户信息
        String token = "authorization";
        //封装用户信息为map添加到redis
        String header = request.getHeader("authorization");
        if (!token.equals(header)){
            String key = LOGIN_USER_KEY + token;
            Map<String, Object> map = BeanUtil.beanToMap(dto, new HashMap<>(),
                    CopyOptions.create()
                            .setIgnoreNullValue(true)
                            .setFieldValueEditor((o1, o2) -> o2.toString())
            );
            stringRedisTemplate.opsForHash().putAll(key,map);
            stringRedisTemplate.expire(key,3,TimeUnit.MINUTES);
        }
        //返回token给前端,封装一个对象拷贝
        AuthorizeVO authorizeVO= dto.asViewObject(AuthorizeVO.class,authorize->{
            authorize.setUsername(username);
            authorize.setExpireTime(new Date());
            authorize.setToken(token);
        });

        return Result.ok(authorizeVO,"登录成功");
    }
}




